/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.oai.metadataRepository.repository;

import cz.mzk.oai.metadataRepository.analysis.marcxml.MarcxmlFilesAnalyzer;
import cz.mzk.oai.metadataRepository.MetadataValidator;
import cz.mzk.oai.metadataRepository.transformation.MetadataTransformer;
import cz.mzk.oai.metadataRepository.Tools;
import cz.mzk.oai.metadataRepository.analysis.ese.EseFilesAnalyzer;
import cz.mzk.oai.metadataRepository.metadata.DateStamp;
import cz.mzk.oai.metadataRepository.metadata.MarcRecord;
import cz.mzk.oai.metadataRepository.metadata.MetadataFormat;
import cz.mzk.oai.metadataRepository.tools.FileTools;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

/**
 * This class represent one directory structure of one collection. 
 * Whole collection and its directory contains files that all belong to the same sets.
 * For example MollMaps-before1800, Oldmaps-after1800, schramVedute-error1800
 * @author Martin Rehanek, Martin.Rehanek@gmail.com
 */
public class MetadataCollection {
    //TODO: sjednotit logovaci hlasky, rozlisit urovne logovani

    public static String MARC_DIR = MetadataFormat.marc21.getDirName();
    //public static String TEI_DIR = "teip5";
    public static String ESE_DIR = MetadataFormat.ese.getDirName();
    public static String DC_DIR = MetadataFormat.oai_dc.getDirName();
    private String rootDir;
    private String name;
    private boolean containsMarcRecords; //at least one record
    private boolean containsEseRecords;
    private boolean containsDcRecords;
    private Map<String, Item> items;
    private Transformer marc2eseTransformer;
    private Transformer marc2dcTransformer;
    private Set<String> sets = new HashSet<String>(2);
    private Properties properties;
    private static String PROPERTIES_FILE_NAME = "collection.properties";
    public static String PROPERTIES_SEPARATOR = " ";
    private MetadataBase base;//TODO: base should be in properties file.
    //and also should be containsMarcRecords, containsEseRecords, containsDcRecords, name
    //The collection should be created just from the rootDir

    /**
     *
     * @param name
     * @param rootDir ABSOLUTE path to the root
     * @param inMarc
     * @param inEse
     * @param marc2eseTempl Path to the xslt template for marc->ese
     * @param inTei
     */
    public MetadataCollection(MetadataBase base, String name, String rootDir, String marc2eseTempl, String marc2dcTempl, boolean containsMarcRecords, boolean containsEseRecords, boolean containsDcRecords) {
        this.base = base;
        this.name = name;
        this.rootDir = rootDir;
        this.containsMarcRecords = containsMarcRecords;
        this.containsEseRecords = containsEseRecords;
        this.containsDcRecords = containsDcRecords;
        initFiles();
        initXslts(marc2eseTempl, marc2dcTempl);
        loadProperties();
    }

    private void initXslts(String marc2eseTempl, String marc2dcTempl) {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        try {
            marc2eseTransformer = transformerFactory.newTransformer(new StreamSource(marc2eseTempl));
            marc2dcTransformer = transformerFactory.newTransformer(new StreamSource(marc2dcTempl));
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        }
    }

    /**
     * Creates directories(if not present)
     * //TODO: load names of files in them and compare to ids from properties
     * if anyone is missing - should act as deleted
     */
    private void initFiles() {
        File rootDirFile = new File(rootDir);
        if (!rootDirFile.exists()) {
            Logger.getLogger(Repository.class.getName()).log(Level.INFO, rootDir + " does not exist, creating");
            rootDirFile.mkdir();
        }
        List<String> filesInMainDir = readFiles(rootDirFile);
        if (containsMarcRecords) {
            //creating $rootDir/$BEFORE_DIR/$MARC_DIR, $rootDir/$AFTER_DIR/$MARC_DIR, $rootDir/$ERROR_DIR/$MARC_DIR if they don't exist
            File marcDirFile = new File(rootDir + File.separator + MARC_DIR);
            if (!filesInMainDir.contains(MARC_DIR)) {
                marcDirFile.mkdir();
                //System.out.println("creating file " + marcDirFile.getAbsolutePath());
                //TODO: nacist idcka a naplnit items podle marc souboru prip. z properties file
            }
        }
        if (containsEseRecords) {
            //creating $rootDir/$BEFORE_DIR/$ESE_DIR, $rootDir/$AFTER_DIR/$ESE_DIR, $rootDir/$ERROR_DIR/$ESE_DIR if they don't exist
            File eseDirFile = new File(rootDir + File.separator + ESE_DIR);
            if (!filesInMainDir.contains(ESE_DIR)) {
                eseDirFile.mkdir();
            }
        }
        if (containsDcRecords) {
            //creating $rootDir/$BEFORE_DIR/$ESE_DIR, $rootDir/$AFTER_DIR/$ESE_DIR, $rootDir/$ERROR_DIR/$ESE_DIR if they don't exist
            File dcDirFile = new File(rootDir + File.separator + DC_DIR);
            if (!filesInMainDir.contains(DC_DIR)) {
                dcDirFile.mkdir();
            }
        }
    }

    private void loadProperties() {
        properties = new Properties();
        String filename = rootDir + File.separator + PROPERTIES_FILE_NAME;
        try {
            properties.load(new FileInputStream(filename));
            loadSets();
            loadItems();
        } catch (FileNotFoundException ex) {
            try {
                Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, "could not find properties file '" + filename + "'");
                File propFile = new File(filename);
                propFile.createNewFile();
            } catch (IOException ex1) {
                Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, "could not create properties file '" + filename + "'");
            }
            loadProperties();
            Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, "created file '" + filename + "'");
        } catch (IOException ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, "error reading properties file '" + filename + "' ", ex);
        }
    }

    /*
     * Loads sets from properties
     */
    private void loadSets() {
        String setList = properties.getProperty("sets");
        if (setList != null && setList.length() != 0) {
            String[] tokens = setList.split(PROPERTIES_SEPARATOR);
            for (int i = 0; i < tokens.length; i++) {
                sets.add((tokens[i]));
                //System.out.println("set " + tokens[i]);
            }
        }
    }

    /**
     * Loads items from the properties
     */
    private void loadItems() {
        items = new HashMap<String, Item>();
        Set<Object> keys = properties.keySet();
        for (Object key : keys) {
            String id = (String) key;
            //this here is not only for discarding incorrectly created identifiers (which should actually never happend)
            //but also for ignoring properties defining sets!
            if (Identifier.isIdentifier(id, base)) {//TODO: enable after isIdentifier() is tested
                String propertyValue = properties.getProperty(id);
                Item item = new Item(id, this, propertyValue);
                items.put(id, item);
            }
        }
    }

    public void updateProperties() {
        properties.setProperty("sets", setsToProperty());
        for (Item item : items.values()) {
            //System.out.println("prop: " + item.toProperty());
            properties.setProperty(item.getId(), item.toProperty());
        }
        String filename = rootDir + File.separator + PROPERTIES_FILE_NAME;
        try {
            properties.store(new FileOutputStream(filename), null);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, "could not find properties file '" + filename + "'", ex);
        } catch (IOException ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, "error writing to properties file '" + filename + "' ", ex);
        }
    }

    private String setsToProperty() {
        StringBuilder builder = new StringBuilder(sets.size() * 2);
        String set = null;
        Iterator it = sets.iterator();
        while (it.hasNext()) {
            set = (String) it.next();
            builder.append(set);
            if (it.hasNext()) {
                builder.append(PROPERTIES_SEPARATOR);//separator is ':'
            }
        }
        return builder.toString();
    }

    /**
     * Removes records from collection (with appropriate reperesentation files)
     * @param recordId
     * @returns removed item or null if it was not present
     */
    public Item removeItem(String itemId) {
        Item removedItem = items.remove(itemId);
        if (removedItem != null) {
            removeRecordFiles(removedItem);
        }
        return removedItem;
    }

    /**
     * Removes marc, ese or other records of item
     * @param id Id of record being removed
     */
    private void removeRecordFiles(Item item) {
        String filename = Tools.idToFileName(item.getId());
        if (item.isInMarc()) {
            String absoluteFilename = FileTools.buildFilePath(rootDir, MARC_DIR, filename);
            if (!FileTools.removeFile(absoluteFilename)) {
                Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "Could not remove file " + absoluteFilename);
            }
        }
        if (item.isInEse()) {
            String absoluteFilename = FileTools.buildFilePath(rootDir, ESE_DIR, filename);
            if (!FileTools.removeFile(absoluteFilename)) {
                Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "Could not remove file " + absoluteFilename);
            }
        }
        if (item.isInDc()) {
            String absoluteFilename = FileTools.buildFilePath(rootDir, DC_DIR, filename);
            if (!FileTools.removeFile(absoluteFilename)) {
                Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "Could not remove file " + absoluteFilename);
            }
        }
    }

    /**
     * Reads files in dir and returns List of their names
     * @param dir direcotry
     * @return List of file names
     */
    private List<String> readFiles(File dir) {
        //System.out.println("readFilenames:dir: " + dir);
        String[] files = dir.list();
        if (files == null) {
            throw new IllegalArgumentException("directory");
        }
        return Arrays.asList(files);
    }

    public String getRootDir() {
        return rootDir;
    }

    public String getName() {
        return name;
    }

    /**
     * Returns true if there allready exists such an item
     * @param record
     * @return
     */
    public boolean contains(String id) {
        return items.containsKey(id);
    }

    /**
     * Item is added to the collection. This means that primary and secondary records are created.
     * If item with same id allready existed, it is replaced if it's datestamp is older than new datestamp (from paramether of method)
     * After adding multiple Items method updateProperties() should be called
     * @param marcDoc Document containing marc21 record
     * @param id id of the item
     * @param datestamp datestamp of the item.
     * @param toEse if the ese record should be created
     * @param toDc if the oai_dc record should be created
     * @return true if item has been added or replaced
     */
    public boolean addItem(Document marcDoc, String id, DateStamp datestamp, boolean toEse, boolean toDc, boolean debug) {
        String filename = Tools.idToFileName(id);
        String marcFile = FileTools.buildFilePath(rootDir, MARC_DIR, filename);
        //String eseFile = toEse ? Tools.buildFilePath(rootDir, ESE_DIR, filename) : null;
        String eseFile = FileTools.buildFilePath(rootDir, ESE_DIR, filename);
        //String dcFile = toDc ? Tools.buildFilePath(rootDir, DC_DIR, filename) : null;
        String dcFile = FileTools.buildFilePath(rootDir, DC_DIR, filename);

        Item presentItem = items.get(id);
        if (presentItem == null) { //item does not yet exists
            if (debug) {
                Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "item [id:" + id + "] not present, creating");
                //System.out.println("new item");
            }
            addNewItem(id, marcDoc, marcFile, toEse ? eseFile : null, toDc ? dcFile : null, datestamp);
            return true;
        } else {//item already exists
            DateStamp presentDatestamp = presentItem.getDateStamp();
            if (debug) {
                System.out.println("item exists");
//              System.out.println("presentDateStamp " + presentDatestamp);
//              System.out.println("dateStamp " + datestamp);
//              System.out.println("presentDateStamp.compareTo(dateStamp): " + presentDatestamp.compareTo(datestamp));
            }
            if (presentDatestamp.compareTo(datestamp) < 0) {//stored item has older datestamp
                replaceItem(presentItem, marcDoc, true, marcFile, toEse, eseFile, toDc, dcFile, datestamp);
                if (debug) {
                    Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "item [id:" + id + "] found newer, replacing");
                    //System.out.println("item newer, replacing");
                }
                return true;
            } else {//stored item is up to date or !(datestamp instanceof DateStamp)
                if (debug) {
                    Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "item [id:" + id + "] found but newer present in repository");
                    //System.out.println("item not newer");
                }
                return false;
            }
        }
    }

    /**
     * Adds an Item that is not yet present in the repository.
     * @param id
     * @param marcDoc
     * @param marcFile name of marc21 file to create or null if it should not be created
     * @param eseFile name of ese file to create or null if it should not be created
     * @param dcFile name of oai_dc file to create or null if it should not be created
     * @param datestamp
     */
    private void addNewItem(String id, Document marcDoc, String marcFile, String eseFile, String dcFile, DateStamp datestamp) {
        boolean toMarc = marcFile != null;
        boolean toEse = eseFile != null;
        boolean toDc = dcFile != null;
        Item item = new Item(id, this, toMarc, toEse, toDc, datestamp);
        if (toMarc) {
            addNewRecord(MetadataFormat.marc21, marcDoc, marcFile);
        }
        if (toEse) {
            addNewRecord(MetadataFormat.ese, marcDoc, eseFile);
        }
        if (toDc) {
            addNewRecord(MetadataFormat.oai_dc, marcDoc, dcFile);
        }
        items.put(id, item);
    }

    private void addNewRecord(MetadataFormat format, Document marcDoc, String filename) {
        try {
            createRecordFile(format, marcDoc, filename);
        } catch (IOException ex) {
            Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "could not create file " + filename);
        }
    }

    /**
     * Replaces already present Item. Records in all formats are removed if they were present. And those that are requested are newly created.
     * @param oldItem
     * @param marcDoc
     * @param toMarc if true the marc21 record will be created
     * @param marcFile name of marc21 file to be created/replaced
     * @param toEse if true the ese record will be created
     * @param eseFile name of ese file to be created/replaced
     * @param toEse if true the oai_dc record will be created
     * @param dcFile name of oai_dc file to be created/replaced
     * @param datestamp
     */
    public void replaceItem(Item oldItem, Document marcDoc, boolean toMarc, String marcFile, boolean toEse, String eseFile, boolean toDc, String dcFile, DateStamp datestamp) {
        //TODO: otestovat
        //throw new RuntimeException("oldDatestamp.compareTo(datestamp) = " + oldDatestamp.compareTo(datestamp));
        resetRecordFile(oldItem.isInMarc(), toMarc, MetadataFormat.marc21, marcDoc, marcFile);
        resetRecordFile(oldItem.isInEse(), toEse, MetadataFormat.ese, marcDoc, eseFile);
        resetRecordFile(oldItem.isInDc(), toDc, MetadataFormat.oai_dc, marcDoc, dcFile);

        oldItem.setDateStamp(datestamp);
        oldItem.setInMarc(toMarc);
        oldItem.setInEse(toEse);
        oldItem.setInDc(toDc);
    }

    /**
     * This method removes all metadata files in secondary formats and
     * recreates them. It should be used when all XSLTs are changed
     * @parem ese if true all ese records will be recreated
     * @parem dc if true all oai_dc records will be recreated
     */
    public void recreateRecords(boolean ese, boolean dc) {
        if (ese) {
            Logger.getLogger(Repository.class.getName()).log(Level.INFO, "collection " + this.getName() + ": recreating ESE records");
        }
        if (dc) {
            Logger.getLogger(Repository.class.getName()).log(Level.INFO, "collection " + this.getName() + ": recreating DC records");
        }
        int counter = 0;
        for (Item item : items.values()) {
            if (++counter % 1000 == 0) {
                System.out.println("allready recreated " + counter + " items");
            }
            recreateRecordsOfItem(item, ese, dc);
        }
        System.out.println(this.getName() + ": recreated " + counter + " items");
        updateProperties();
    }

    /**
     * This metod removes records in secondary formats and recrates them from primary format.
     * After multiple invocation of this method the updateProperties() must be invoked
     * @param item item
     * @param ese if true ese record will be recreated
     * @param dc if true oai_dc record will be recreated
     */
    private void recreateRecordsOfItem(Item item, boolean ese, boolean dc) {
        String filename = Tools.idToFileName(item.getId());
        String marcFilename = FileTools.buildFilePath(rootDir, MARC_DIR, filename);
        String eseFilename = FileTools.buildFilePath(rootDir, ESE_DIR, filename);
        String dcFilename = FileTools.buildFilePath(rootDir, DC_DIR, filename);

        File marcFile = new File(marcFilename);
        DateStamp dateStamp = new DateStamp();

        item.setDateStamp(dateStamp);
        try {
            SAXReader reader = new SAXReader();
            Document marcDoc = reader.read(marcFile);
            MarcRecord marcRecord = new MarcRecord(marcDoc, item.getId());
            if (ese) {
                boolean hasPreview = marcRecord.hasPreview();
                item.setInEse(hasPreview);
                if (hasPreview) {
                    //MetadataTransformer.transform(marcFile.getAbsolutePath(), eseFilename, marc2eseTransformer);
                    MetadataTransformer.transform(marcDoc, eseFilename, marc2eseTransformer);
                } else {
                    //TODO: odstranit a oznacit DELETED. Momentalne je tam plno ese souboru bez nahledu
                }
            }
            if (dc) {
                MetadataTransformer.transform(marcFile.getAbsolutePath(), dcFilename, marc2dcTransformer);
                item.setInDc(true);
            }
        } catch (DocumentException ex) {
            Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, null, ex);
        }
        //carefull, updateProperties is not called here.
    }

    private void createRecordFile(MetadataFormat format, Document marcDoc, String filename) throws IOException {
        switch (format) {
            case marc21:
                writeXmlFile(marcDoc, filename);
                break;
            case ese:
                MetadataTransformer.transform(marcDoc, filename, marc2eseTransformer);
                break;
            case oai_dc:
                MetadataTransformer.transform(marcDoc, filename, marc2dcTransformer);
                break;
            default:
                return;
        }
        Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "File " + filename + " has been created");
    }

    /**
     * Record in specified format is removed, created or updated
     * @param wasPresent if true the record is present (from older version an will be therefore removed)
     * @param isRequested if true the record will be created (or updated if existed)
     * @param format 
     * @param marcDoc
     * @param filename
     */
    private void resetRecordFile(boolean wasPresent, boolean isRequested, MetadataFormat format, Document marcDoc, String filename) {
        if (wasPresent) {//remove old file
            if (FileTools.removeFile(filename)) {
                Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "file " + filename + " has been removed");
            } else {
                Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "could not remove file " + filename);
            }
        }
        if (isRequested) {//create new file
            try {
                createRecordFile(format, marcDoc, filename);
            } catch (IOException ex) {
                Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, "could not create file " + filename);
            }
        }
    }

    private void writeXmlFile(Document doc, String filename) throws IOException {
        OutputFormat format = OutputFormat.createCompactFormat();
        XMLWriter writer = new XMLWriter(new FileWriter(filename, false), format);
        writer.write(doc);
        writer.close();
    }

    /**
     * @deprecated
     */
    private void createMarcFile(String fileFrom, String fileTo) {
        try {
            String command = null;
            if ("Windows 7".equals(System.getProperty("os.name"))) {
                command = "copy " + fileFrom + " " + fileTo;
                //FIXME: toto porad nechce fungovat. Ani s prikazem "Copy-Item"
            } else {
                command = "cp " + fileFrom + " " + fileTo;
            }
            //System.out.println("command: " + command);
            Process process = Runtime.getRuntime().exec(command);
            process.waitFor();
            //logging errors from command
            if (process.exitValue() != 0) {
                StringBuilder builder = new StringBuilder();
                builder.append("Error in '");
                builder.append(command);
                builder.append("':");
                builder.append(Tools.errorstreamToString(process));
                Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, builder.toString());
            }
        } catch (InterruptedException ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Repository.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void addToSet(String setSpec) {
        sets.add(setSpec);
        updateProperties();
    }

    /**
     * Should be called after finished working with collection
     */
    public void close() {
        updateProperties();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("===================================\n");
        builder.append(name);
        builder.append("\n-----------------------------------\n");

        //System.out.println("items: " + items.size());
        int marcRecords = 0;
        int eseRecords = 0;
        for (Item item : items.values()) {
            marcRecords += item.isInMarc() ? 1 : 0;
            eseRecords += item.isInEse() ? 1 : 0;
        }

        builder.append("items: ");
        builder.append(items.size());
        builder.append('\n');

        builder.append("marc records : ");
        builder.append(marcRecords);
        builder.append('\n');

        builder.append("ese records: ");
        builder.append(eseRecords);
        builder.append('\n');

        builder.append("sets: ");
        for (String set : sets) {
            builder.append(set);
            builder.append(' ');
        }
        //builder.append("properties:\n");
        //builder.append(properties.toString());
        builder.append('\n');
        builder.append('\n');
        return builder.toString();
    }

    public Set<String> getSets() {
        return Collections.unmodifiableSet(sets);
    }

    /*
     *@return number of items
     */
    public int size() {
        return items.size();
    }

    /**
     * @param format
     * @return list of those items that have recored in format. Or null if collection doesn't support format
     */
    public List<Item> getItems(MetadataFormat format) {
        if (supportsFormat(format)) {
            List<Item> ids = new ArrayList<Item>();
            for (Item item : items.values()) {
                boolean hasRecord = false;
                switch (format) {
                    case marc21:
                        hasRecord = item.isInMarc();
                        break;
                    case ese:
                        hasRecord = item.isInEse();
                        break;
                    case oai_dc:
                        hasRecord = item.isInDc();
                        break;
                }
                if (hasRecord) {
                    ids.add(item);
                }
            }
            return ids;
        } else {
            return null;
        }
    }

    public boolean supportsFormat(MetadataFormat format) {
        boolean supportsFormat = false;
        switch (format) {
            case marc21:
                supportsFormat = containsMarcRecords;
                break;
            case ese:
                supportsFormat = containsEseRecords;
                break;
            case oai_dc:
                supportsFormat = containsDcRecords;
                break;
        }
        return supportsFormat;
    }

    /**
     *
     * @param id
     * @param metadataFormat
     * @return Record as a string
     * @throws NoSuchRecordException If there is no such record in required format (wrong format, missing record, etc)
     */
    public Document getRecord(String id, MetadataFormat format, boolean validate) throws NoSuchRecordException {
        Item item = items.get(id);
        if (item == null) {//no such item
            throw new NoSuchRecordException();
        }
        switch (format) {
            case marc21:
                if (item.isInMarc()) {
                    break;
                } else {//item not in marc
                    throw new NoSuchRecordException();
                }
            case ese:
                if (item.isInEse()) {
                    break;
                } else {//item not in ese
                    throw new NoSuchRecordException();
                }
            case oai_dc:
                if (item.isInDc()) {
                    break;
                } else {//item not in dc
                    throw new NoSuchRecordException();
                }
            default:
                throw new NoSuchRecordException();
        }
        return loadRecordFile(id, format, validate);
    }

    private Document loadRecordFile(String id, MetadataFormat format, boolean validate) throws NoSuchRecordException {
        Document doc = null;
        String filename = FileTools.buildFilePath(rootDir, format.getDirName(), Tools.idToFileName(id));
        File inFile = new File(filename);
        try {
            SAXReader reader;
            if (validate) {
                reader = MetadataValidator.instanceOf().getValidatingSAXReader(format);
            } else {
                reader = new SAXReader();
            }
            doc = reader.read(inFile);
        } catch (DocumentException ex) {
            Logger.getLogger(MetadataCollection.class.getName()).log(Level.SEVERE, null, ex);
            throw new NoSuchRecordException();
        }
        return doc;
    }

    public Item getItem(String id) {
        return items.get(id);
    }

    /*
     * @return number of records in metadataFormat
     */
    public int records(String metadataFormat) {
        int result = 0;
        boolean marc = "marc21".equals(metadataFormat);
        boolean ese = "ese".equals(metadataFormat);
        boolean dc = "oai_dc".equals(metadataFormat);
        if (!(marc || ese || dc)) {
            throw new IllegalArgumentException("no such a format");
        }
        for (Item item : items.values()) {
            if (marc && item.isInMarc()) {
                result++;
            }
            if (ese && item.isInEse()) {
                result++;
            }
            if (dc && item.isInDc()) {
                result++;
            }
        }
        return result;
    }

    /**
     * test method
     */
    public void analyzeDatafield856() {
        String marcDir = rootDir + File.separator + MARC_DIR;
        String logFile = rootDir + "datafield856test.log";
        MarcxmlFilesAnalyzer.analyzeDatafield856(marcDir, logFile);
    }

    /**
     * test method
     */
    public void analyzeDatafield040a() {
        String marcDir = rootDir + File.separator + MARC_DIR;
        String logFile = rootDir + "datafield040test.log";
        MarcxmlFilesAnalyzer.analyzeDatafield040b(marcDir, logFile);
    }

    public void analyzeEseDcLanguage() {
        String eseDir = rootDir + File.separator + ESE_DIR;
        //String logFile = rootDir + "EseDcLanguageElementtest.log";
        EseFilesAnalyzer.analyzeDcLanguage(eseDir);
    }

    public void analyzeEseEuropeanaIsShownBy() {
        String eseDir = rootDir + File.separator + ESE_DIR;
        //String logFile = rootDir + "EseDcLanguageElementtest.log";
        EseFilesAnalyzer.analyzeEuropeanaIsShownBy(eseDir);
    }

    public void analyzeEseEuropeanaIsShownAt() {
        String eseDir = rootDir + File.separator + ESE_DIR;
        //String logFile = rootDir + "EseDcLanguageElementtest.log";
        EseFilesAnalyzer.analyzeEuropeanaIsShownAt(eseDir);
    }

    public void analyzeEseEuropeanaObject() {
        String eseDir = rootDir + File.separator + ESE_DIR;
        //String logFile = rootDir + "EseDcLanguageElementtest.log";
        EseFilesAnalyzer.analyzeEuropeanaObject(eseDir);
    }

    public MetadataBase getBase() {
        return base;
    }
}
